import axios from "axios";

/*
 参考资料:
    https://blog.csdn.net/qq_41755979/article/details/125725807
    https://github.com/rany2/edge-tts/blob/142b4f64573111876290e5be698a299fbc679304/src/edge_tts/communicate.py
    https://github.com/Migushthe2nd/MsEdgeTTS
*/

const
    TTS_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4',
    TTS_WSS_URL = `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TTS_TOKEN}`,
    TTS_LST_URL = `https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=${TTS_TOKEN}`,
    headers = {Authority: 'speech.platform.bing.com'};

//随机生成一个请求ID
function randomString(len, charSet) {
    charSet = charSet || '0123456789ABCDEF';
    var randomString = '';
    for (var i = 0; i < len; i++) {
        var randomPoz = Math.floor(Math.random() * charSet.length);
        randomString += charSet.substring(randomPoz,randomPoz+1);
    }
    return randomString;
}

//const isEdge = navigator.userAgent.includes('Edg/')

window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
const forceMs = true;
function EdgeTtsEngine()
{
    let voices = speechSynthesis.getVoices(), streams = {}, useDefault = false, selected = null;
    this.ready = false;
    speechSynthesis.onvoiceschanged = (v)=>{
        if(this.ready) return;
        let all = speechSynthesis.getVoices(), cn = all.filter(v=>v.lang.includes('zh-CN')), 
        ol = cn.filter(v=>v.localService === false && !v.name.includes('Google'));
        if(ol.length == 0 || forceMs){
            axios.get(TTS_LST_URL, {headers}).then(({data:res})=>{
                let v = [];
                if(res.constructor === Array){
                    v = res.filter(el=>el.Locale.includes('zh-CN'))
                }else{
                    v = [];
                }

                if(v.length > 0) {
                    selected = v[0];
                    useDefault = false;
                    console.log('Use Microsoft Edge speechSynthesis Engine.')
                    this.ready = true;
                } else {
                    selected = cn.length > 0 ? cn[0] : selected = all[0];
                    this.ready = useDefault = true;
                    console.warn("Get Microsoft Edge speechEngine Failed.")
                }
            });
        }else{
            voices = ol; selected = ol[0];
            useDefault = true;
            console.log('Use Browser speechSynthesis Engine.');
            this.ready = true;
        }
    }

    // 兼容火狐浏览器.
    if(voices.length > 0){ speechSynthesis.onvoiceschanged(null)}

    this.speak = (words, cache) =>{
        const playSound = (u8) => {
            let audioCtx = new window.AudioContext();;
            audioCtx.decodeAudioData(u8.buffer, function(buffer){
                let source = audioCtx.createBufferSource();
                source.buffer = buffer;
                source.connect(audioCtx.destination);
                source.loop = false;
                source.start(0); //立即播放
            })
        }, 
        speakOnline = ()=>{
            let ws = new WebSocket(TTS_WSS_URL);
            ws.binaryType = 'arraybuffer';

            // voice.SuggestedCodec = 'webm-24khz-16bit-mono-opus';
            //console.log(voice.SuggestedCodec);
            ws.onopen = ()=>{
                let requestId = randomString(32);
                ws.send(
    `Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n{
        "context": {
            "synthesis": {
                "audio": {
                    "metadataoptions": {
                        "sentenceBoundaryEnabled": "false",
                        "wordBoundaryEnabled": "false"
                    },
                    "outputFormat": "${selected.SuggestedCodec}"
                }
            }
        }
    }
    `
                );
                ws.send(`X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\nPath:ssml\r\n\r\n
    <speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='https://www.w3.org/2001/mstts' xml:lang='{lang}'>
        <voice name='${selected.Name}'>${words}</voice>
    </speak>`)
            };

            // 消息处理
            ws.onmessage = async (e)=>{
                let str = e.data, cmd = {}, offset = 0 , u8, header, mime = '';

                if(e.data instanceof ArrayBuffer){
                    header = new Uint8Array(e.data).slice(0, 3)
                    if (header[0] === 0x00 && header[1] === 0x67 && header[2] === 0x58) {
                        return;
                    }
                    u8 = new Uint8Array(e.data).slice(2);
                    str = String.fromCharCode.apply(null, u8);
                }
                str.split("\r\n").forEach(line => {
                    let r = line.match(/^(X\-RequestId|Content\-Type|X\-StreamId|Path):(.*)/);
                    if(null !== r && r.length == 3){
                        cmd[ r[1] ] =  r[2];
                        offset += r[1].length + r[2].length + 3
                    }

                    if(false === mime && cmd['Content-Type']){
                        console.log('Mime = ', mime);
                        mime = cmd['Content-Type'];
                    }
                });

                switch(cmd.Path){
                case 'turn.start':  //开始
                    streams[cmd.streamId] = {frames:[], length:0, type:null};
                    break;
                case 'response':    //忽略掉
                    break;
                case 'audio':
                    if(u8){
                        u8 = u8.slice(offset);
                        let f = streams[cmd.streamId];
                        f.frames.push(u8);
                        f.length += u8.length;
                        if(null === f.type){
                            f.type = cmd['Content-Type'];
                        }
                    }
                    break;
                case 'turn.end':    //完成
                    playFrames(cmd.streamId, mime);
                    ws.close();
                    break;
                }
            }
            ws.onclose = ()=>{}
        },
        
        playFrames = (streamId)=>{
            let stream = streams[ streamId ], length = 0, offset = 0, u8;
            u8 = new Uint8Array(stream.length);
            stream.frames.forEach(v=>{u8.set(v, offset); offset+=v.length})
            delete streams[ streamId ];

            playSound(u8)
        };

        if(useDefault){
            speechSynthesis.cancel();
            speechSynthesis.speak(Object.assign(new SpeechSynthesisUtterance, {voice: selected}, {text: words, volume: 1}));
            return;
        }
        speakOnline();
    }
}

const EdgeVoice = new EdgeTtsEngine()

const speak = (words, cache)=>{
    if(!EdgeVoice.ready){
        speechSynthesis.cancel();
        speechSynthesis.speak(Object.assign(new SpeechSynthesisUtterance, {text: words, volume: 1}));
        return;
    }

    EdgeVoice.speak(words, cache);
}


export {
    EdgeVoice,
    speak
}
